// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include "Deveco/ParseEnvironmentVariable.h"

#include "Deveco/CjpmRunParameter.h"

using namespace CangjieMigrator;
using namespace CangjieMigrator::Deveco;

using Platform::EnvironmentVariable;

const CjpmRunParameter CjpmRunParameter::EMPTY = CjpmRunParameter("", {}, "", false);

CjpmRunParameter::CjpmRunParameter(
    std::string name, std::vector<EnvironmentVariable> environment, std::string command, bool success)
    : name(std::move(name)), environment(std::move(environment)), command(std::move(command)), success(success)
{}

bool CjpmRunParameter::IsValid() const
{
    return !name.empty();
}

namespace {

namespace ParseFromLogs {

bool StartsWith(const std::string& str, const std::string& prefix)
{
    if (prefix.size() > str.size()) {
        return false;
    }
    return str.compare(0, prefix.size(), prefix) == 0;
}

class StateMachine {
    enum class State {
        COMMON,
        ENVIRONMENT_VARIABLE,
        COMMAND,
        RESULT,
    };

    std::vector<EnvironmentVariable> environment;
    std::string command;
    bool success;

    std::vector<CjpmRunParameter> results;

    State state;

    CjpmRunParameter Generate() const;

    template <State currentState>
    State StateTransition(const Log& log);

public:
    StateMachine();

    void Process(const Log &log);
    void Process(const std::vector<Log> &logs);

    std::vector<CjpmRunParameter> TakeResult();
};

CjpmRunParameter StateMachine::Generate() const
{
    std::string name = "";
    for (const auto &v : environment) {
        if (v.name == "CURRENT_PACKAGE_NAME") {
            name = v.value;
        }
    }
    return CjpmRunParameter(name, environment, command, success);
}

StateMachine::StateMachine()
    : environment({}), command({}), success(false), results({}), state(State::COMMON)
{}

template<StateMachine::State currentState>
StateMachine::State StateMachine::StateTransition(const Log &log)
{
    throw std::logic_error("Should not use default transition function.");
}

template<>
StateMachine::State StateMachine::StateTransition<StateMachine::State::COMMON>(const Log &log)
{
    if (log.content.empty()) {
        return State::COMMON;
    }

    if (log.content[0] != "Use tool [NodeEnv]") {
        return State::COMMON;
    }

    environment = ParseEnvironmentVariableLog(log.content);
    if (environment.empty()) {
        return State::COMMON;
    }

    return State::ENVIRONMENT_VARIABLE;
}

const std::string COMMAND_START = "Cangjie build command: ";
const size_t COMMAND_START_LENGTH = COMMAND_START.size();

template<>
StateMachine::State StateMachine::StateTransition<StateMachine::State::ENVIRONMENT_VARIABLE>(const Log &log)
{
    if (log.content.size() != 1) {
        return State::COMMON;
    }

    const auto line = log.content[0];
    if (!StartsWith(line, COMMAND_START)) {
        return State::COMMON;
    }

    command = line.substr(COMMAND_START_LENGTH);
    if (command.empty()) {
        return State::COMMON;
    }

    return State::COMMAND;
}

template<>
StateMachine::State StateMachine::StateTransition<StateMachine::State::COMMAND>(const Log &log)
{
    success = !log.content.empty() && log.content[0] == "cjpm build success";
    results.emplace_back(Generate());
    return State::COMMON;
}

void StateMachine::Process(const Log &log)
{
    switch (state) {
        case State::COMMON:
            state = StateTransition<State::COMMON>(log);
            break;
        case State::ENVIRONMENT_VARIABLE:
            state = StateTransition<State::ENVIRONMENT_VARIABLE>(log);
            break;
        case State::COMMAND:
            state = StateTransition<State::COMMAND>(log);
            break;
        case State::RESULT:
            state = StateTransition<State::RESULT>(log);
            break;
        default:
            throw std::logic_error("The state machine has entered an unknown state.");
    }
}

void StateMachine::Process(const std::vector<Log> &logs)
{
    for (const auto &log : logs) {
        Process(log);
    }
}

std::vector<CjpmRunParameter> StateMachine::TakeResult()
{
    return std::move(this->results);
}

} // namespace ParseFromLogs

} // namespace

std::vector<CjpmRunParameter> CjpmRunParameter::ParseFromLogs(const std::vector<Log> &logs)
{
    auto machine = ParseFromLogs::StateMachine();
    machine.Process(logs);
    return machine.TakeResult();
}
